<!--
 * @Description: 数据表格
 * @Author: DHL
 * @Date: 2021-12-23 23:00:45
 * @LastEditors: DHL
 * @LastEditTime: 2024-02-08 15:13:00
-->

<template>
  <div class="tw-table-wrapper">
    <vxe-grid
      ref="tableInstance"
      v-bind="tableOptions"
      v-on="tableEvents"
      v-loading="props.loading"
      element-loading-background="var(--tw-mask-color)"
    >
      <!-- 动态插槽 -->
      <template v-slot:[slotName]="{ row }" v-for="slotName in slotKeys">
        <slot :name="slotName" :row="row"></slot>
      </template>

      <!-- 自定义插槽 数据字典插槽 -->
      <template #slotDict="{ row, column }">
        <tw-dic-name :dicKey="column.params.dict" :dicValue="row[column.field]"></tw-dic-name>
      </template>

      <!-- 自定义插槽 pager 插槽-->
      <template #pager>
        <vxe-pager
          :layouts="tablePageLayouts"
          v-model:current-page="tablePage.currentPage"
          v-model:page-size="tablePage.pageSize"
          v-model:page-sizes="tablePage.pageSizes"
          :total="tablePage.total"
          @page-change="handlePageChange"
        >
          <template #left>
            <el-button circle title="列设置" @click="handleOpenCustomColumn($event)">
              <svg-icon
                iconClass="arrangement"
                className="tw-table-page-left-icon"
                color="var(--color-primary)"
              ></svg-icon>
            </el-button>

            <el-button
              circle
              title="导出Excel"
              @click="handleOpenExportExcel()"
              v-auth="exportExcelAuthKey"
            >
              <svg-icon
                iconClass="microsoft-Excel"
                className="tw-table-page-left-icon"
                color="var(--color-primary)"
              ></svg-icon>
            </el-button>

            <slot name="tablePagerBtns"></slot>
          </template>
        </vxe-pager>
      </template>
    </vxe-grid>

    <!-- 列设置 -->
    <tw-dialog
      v-model="customColumn.visible"
      title="列设置"
      width="500px"
      height="60vh"
      customClass="tw-custom-column-wrapper"
      @close="handleCloseCustomColumn"
    >
      <el-table
        :data="customColumn.tableColumns"
        size="small"
        style="width: 100%; margin-bottom: 20px"
        row-key="id"
        border
        default-expand-all
      >
        <el-table-column prop="title" label="列名" />
        <el-table-column prop="isVisible" label="可见" width="50" align="center">
          <template #default="scope">
            <el-checkbox
              v-if="handleShowCheckBox(scope.row)"
              v-model="scope.row.visible"
            ></el-checkbox>
          </template>
        </el-table-column>
        <el-table-column prop="colSeq" label="序号" width="120">
          <template #default="scope">
            <el-input v-model="scope.row.colSeq" size="small" placeholder="请输入序号" />
          </template>
        </el-table-column>
        <el-table-column prop="width" label="列宽" width="120">
          <template #default="scope">
            <el-input v-model="scope.row.width" size="small" placeholder="请输入宽度" />
          </template>
        </el-table-column>
      </el-table>

      <template #footer>
        <el-button type="primary" @click="handleSubmitCustomColumn()">确 认</el-button>
        <el-button type="warning" @click="handleResetCustomColumn()">还 原</el-button>
        <el-button @click="handleCloseCustomColumn()">取 消</el-button>
      </template>
    </tw-dialog>

    <!-- 导出Excel -->
    <tw-dialog
      v-model="exportExcel.visible"
      title="导出Excel"
      width="400px"
      height="200px"
      heightContent="150px"
      :showZoom="false"
      customClass="tw-export-excel-wrapper"
      @close="handleCloseExportExcel"
    >
      <el-radio-group v-model="exportExcel.dataRange" size="large">
        <el-radio value="currentData" size="large">导出当前页</el-radio>
        <el-radio value="allData" size="large">导出全部数据</el-radio>
      </el-radio-group>

      <template #footer>
        <el-button type="primary" @click="handleExportExcel()">导 出</el-button>
        <el-button @click="handleCloseExportExcel()">取 消</el-button>
      </template>
    </tw-dialog>
  </div>
</template>

<script setup lang="ts">
  import { ElMessage } from 'element-plus'
  import { assign, cloneDeep, merge, difference } from 'lodash-es'
  import { computed, nextTick, onMounted, reactive, ref, useSlots, watch } from 'vue'
  import { VxeGridInstance, VxePagerDefines, VxePagerPropTypes, VxeTableDefines } from 'vxe-table'
  import { BaseModel, Result, ResultPage } from '@/model/baseModel'
  import { useTableSettingStore } from '@/store/modules/tableSetting'
  import { TwTableEvents, TwTableOptions } from '@/types/table'
  import { isNotEmpty } from '@/utils/isUtils'
  import { log } from '../../utils/logUtils'
  import { stringUtils } from '@/utils/stringUtils'
  import { arrayRemoveByPropertyValue, arraySerialize } from '@/utils/arrayUtils'
  import BigNum from '@/utils/bigNum'

  /**
   * 用户自定义配置
   */
  const tableSettingStore = useTableSettingStore()

  //////////////////////////////////////////////////
  // 接收参数
  //////////////////////////////////////////////////
  interface Props {
    /**
     * 配置项
     */
    options: TwTableOptions

    /**
     * 事件
     */
    events?: TwTableEvents

    /**
     * 数据
     */
    data: any

    /**
     * 加载效果
     */
    loading: boolean

    /**
     * 导出Excel按钮权限控制key
     */
    exportExcelAuthKey?: string
  }

  const props = withDefaults(defineProps<Props>(), {
    options: () => {
      return { id: '' }
    },
    events: () => {
      return {}
    },
    data: () => {
      return {}
    },
    loading: false,
    exportExcelAuthKey: 'withoutPermission'
  })

  const emit = defineEmits(['search', 'searchSum', 'exportExcel'])

  //////////////////////////////////////////////////
  // 插槽
  //////////////////////////////////////////////////

  /**
   * 插槽集合
   */
  const slots = useSlots()

  /**
   * 插槽名称集合
   */
  const slotKeys = computed(() => {
    const slotKeys = Object.keys(slots)
    return slotKeys
  })

  //////////////////////////////////////////////////
  // 表格配置
  //////////////////////////////////////////////////

  /**
   * 表格实例
   */
  const tableInstance = ref({} as VxeGridInstance)

  /**
   * 表格参数
   */
  let tableParams = {
    columns: [] // 开发列配置
  }

  /**
   * 默认设置
   */
  const defaultOptions: TwTableOptions = {
    id: '',
    autoLoad: true,
    border: true,
    showHeaderOverflow: true,
    showOverflow: true,
    keepSource: false,
    height: 'auto',
    autoResize: true,
    stripe: true,
    align: 'left',
    resizeConfig: {
      refreshDelay: 200
    },
    rowConfig: {
      keyField: 'id',
      isHover: true
    },
    columnConfig: {
      resizable: true
    },
    pagerConfig: {
      enabled: true
    },
    sortConfig: {
      trigger: 'cell',
      multiple: true,
      remote: true
    },
    checkboxConfig: {
      highlight: true
    },
    scrollX: {
      enabled: true,
      gt: 60 // 当列大于指定数量时自动触发启用虚拟滚动
    }
  }

  /**
   * 表格配置
   */
  const tableOptions = reactive<TwTableOptions>(cloneDeep(defaultOptions))

  //////////////////////////////////////////////////
  // 表格事件
  //////////////////////////////////////////////////

  /**
   * 默认事件
   */
  const defaultEvents: TwTableEvents = {
    sortChange: () => {
      handleSearch()
    }
  }

  /**
   * 表格事件
   */
  const tableEvents = reactive<TwTableEvents>(merge(cloneDeep(defaultEvents), props.events))

  //////////////////////////////////////////////////
  // 分页
  //////////////////////////////////////////////////

  /**
   * 分页配置
   */
  const tablePage = reactive({
    pages: 0, // 总页数
    total: 0, // 总条数
    currentPage: 1, // 当前页
    pageSize: 20, // 每页显示条数
    pageSizes: [20, 50, 100, 200]
  })

  /**
   * 分页组件布局
   */
  const tablePageLayouts = computed(() => {
    let layouts: VxePagerPropTypes.Layouts = ['Total']

    if (tablePage.pages > 0) {
      layouts = [
        'Sizes',
        'PrevJump',
        'PrevPage',
        'Number',
        'NextPage',
        'NextJump',
        'FullJump',
        'Total'
      ]
    }

    return layouts
  })

  /**
   * 操作分页
   */
  function handlePageChange(pageParams: VxePagerDefines.PageChangeEventParams) {
    tablePage.currentPage = pageParams.currentPage
    tablePage.pageSize = pageParams.pageSize

    handleSearch()
  }

  //////////////////////////////////////////////////
  // 监听表格数据变化
  //////////////////////////////////////////////////
  /**
   * 监听表格数据
   */
  watch(
    () => props.data,
    (result) => {
      // 返回结果没有分页参数
      if (result instanceof Array) {
        tableOptions.data = result as unknown as any[]
        Object.assign(tablePage, { pages: 0, total: tableOptions.data.length })
      } else if (result.data instanceof Array) {
        const _result = result as Result<BaseModel>
        tableOptions.data = _result.data as unknown as any[]
        Object.assign(tablePage, { pages: 0, total: tableOptions.data.length })
      } else if (result.data?.list instanceof Array) {
        const _result = result as ResultPage<BaseModel>
        tableOptions.data = _result.data?.list
        Object.assign(tablePage, { pages: _result.data?.pages, total: _result.data?.total })
      } else {
        log.error('TwTable结果解析', '未知数据类型')
      }
    },
    { deep: true }
  )

  //////////////////////////////////////////////////
  // 函数
  //////////////////////////////////////////////////

  /**
   * 获取排序参数
   */
  function getSortParams() {
    // 排序
    let sortParams = {}
    const sort = tableInstance.value.getSortColumns()
    if (sort.length > 0) {
      let _sort: Array<string> = []
      sort.forEach((item) => {
        _sort.push(`${stringUtils.toLine(item?.field)} ${item?.order}`)
      })

      sortParams = {
        sort: _sort.join(',')
      }
    } else {
      const defaultSort = tableOptions.sortConfig?.defaultSort
      if (defaultSort) {
        if (defaultSort instanceof Array) {
          let _sort: Array<string> = []
          defaultSort.forEach((item) => {
            _sort.push(`${stringUtils.toLine(item?.field)} ${item?.order}`)
          })

          sortParams = {
            sort: _sort.join(',')
          }
        } else {
          sortParams = {
            sort: `${stringUtils.toLine(defaultSort?.field)} ${defaultSort?.order}`
          }
        }
      }
    }

    return sortParams
  }

  /**
   * 查询数据
   * @param isSearchSum 是否查询合计
   */
  function handleSearch(isSearchSum = false) {
    nextTick(() => {
      // 排序参数
      let queryParams = getSortParams()

      // 分页参数
      if (tableOptions.pagerConfig?.enabled) {
        assign(queryParams, {
          pageNum: tablePage.currentPage,
          pageSize: tablePage.pageSize
        })
      }

      // 不分页查询
      if (tableOptions.pagerConfig?.enabled === false) {
        assign(queryParams, {
          enable: false
        })
      }

      emit('search', queryParams)
      if (isSearchSum && tableOptions.showFooter) {
        emit('searchSum', queryParams)
      }
    })
  }

  /**
   * 查询
   */
  function handleQuery() {
    tablePage.currentPage = 1
    handleSearch(true)
  }

  /**
   * 重新加载
   */
  function handleReload() {
    handleSearch(true)
  }

  /**
   * 获取选中的行数据
   * @param isFull  用于 checkbox-config.reserve，获取已保留选中的行数据
   */
  function getSelectRows(isFull = false): Array<any> {
    let rtn = tableInstance.value.getRadioRecord()
    if (rtn) {
      return rtn
    }

    rtn = tableInstance.value.getCheckboxRecords()
    if (tableOptions.checkboxConfig?.reserve && isFull) {
      rtn.push(...tableInstance.value.getCheckboxReserveRecords())
    }

    return rtn
  }

  //////////////////////////////////////////////////
  // 自定义列
  //////////////////////////////////////////////////

  /**
   * 自定义列属性
   */
  const customColumn = reactive({
    visible: false,
    tableColumns: [] as Array<any>, // 表格列配置
    myColumns: [] as Array<any> // 自定义列配置
  })

  /**
   * 显示自定义列面板
   */
  function handleOpenCustomColumn(e: MouseEvent) {
    // 用户配置的列
    const myColumns = tableSettingStore.getCustomColumn(tableOptions.id)

    // 比对结果
    const diffColumnsField = diffColumnsFieldFun()

    if (diffColumnsField.length > 0) {
      diffColumnsField.forEach((field) => {
        arrayRemoveByPropertyValue(myColumns, 'field', field)
      })
    }

    customColumn.tableColumns = mergeColumnsFun(tableParams.columns, myColumns)

    // 获取表格渲染列宽
    customColumn.tableColumns.forEach((item: any) => {
      if (isNotEmpty(item.field)) {
        const columnInfo: Nullable<VxeTableDefines.ColumnInfo> =
          tableInstance.value.getColumnByField(item.field)
        if (columnInfo) {
          item.width = columnInfo.renderWidth
        }
      }
    })

    customColumn.visible = true
  }

  /**
   * 用户配置列 与 开发配置列进行比对
   */
  function diffColumnsFieldFun() {
    // 用户配置的列
    const myColumns = tableSettingStore.getCustomColumn(tableOptions.id)

    if (myColumns && myColumns.length > 0) {
      // 用户配置列的字段集合
      const myColumnsField = recursionMyColumns(myColumns)

      // 开发配置列的字段集合
      const tableColumnsField = recursionTableColumns(tableParams.columns)

      // 比对结果
      const diffColumnsField = difference(myColumnsField, tableColumnsField)

      return diffColumnsField
    }

    return []
  }

  /**
   * 递归表格配置的列
   * @param columns
   */
  function recursionTableColumns(columns: Array<any>) {
    let colField: Array<string> = []
    colField.concat(arraySerialize(columns, 'field'))

    columns.forEach((item) => {
      colField.push(item.field)
      if (item.children && item.children.length > 0) {
        colField.concat(recursionTableColumns(item.children))
      }
    })

    return colField
  }

  /**
   * 递归用户配置的列
   * @param columns
   */
  function recursionMyColumns(columns: Array<any>) {
    let colField: Array<string> = []
    colField.concat(arraySerialize(columns, 'field'))

    columns.forEach((item) => {
      colField.push(item.field)
      if (item.children && item.children.length > 0) {
        colField.concat(recursionMyColumns(item.children))
      }
    })

    return colField
  }

  /**
   * 用户配置列可见组件是否显示
   * @param columen
   */
  function handleShowCheckBox(columen: { children: [] }) {
    if (columen.children && columen.children.length > 0) {
      return false
    }
    return true
  }

  /**
   * 提交列设置
   */
  function handleSubmitCustomColumn() {
    tableSettingStore.setCustomColumn({ [tableOptions.id]: customColumn.tableColumns })
    setTableOptionsColumns()
    handleRefreshColumn()
    ElMessage.success('列设置成功')
  }

  /**
   * 还原列设置
   */
  function handleResetCustomColumn() {
    tableSettingStore.removeCustomColumn(tableOptions.id)
    setTableOptionsColumns()
    handleRefreshColumn()

    customColumn.tableColumns = tableParams.columns
    // 获取表格渲染列宽
    customColumn.tableColumns.forEach((item: any) => {
      if (isNotEmpty(item.field)) {
        const columnInfo: Nullable<VxeTableDefines.ColumnInfo> =
          tableInstance.value.getColumnByField(item.field)
        if (columnInfo) {
          item.width = columnInfo.renderWidth
        }
      }
    })
    ElMessage.success('列设置还原成功')
  }

  /**
   * 关闭列设置
   */
  function handleCloseCustomColumn() {
    customColumn.visible = false
  }

  /**
   * 刷新列设置并渲染
   */
  function handleRefreshColumn() {
    nextTick(() => {
      if (tableOptions.columns) {
        tableInstance.value.loadColumn(tableOptions.columns)
        // tableInstance.value.refreshColumn()
      }
    })
  }

  /**
   * 获取表格可渲染配置列
   */
  function setTableOptionsColumns() {
    let tableOptionsColumns = [] as any

    // 用户配置的列
    const myColumns = tableSettingStore.getCustomColumn(tableOptions.id)

    // 比对结果
    const diffColumnsField = diffColumnsFieldFun()

    if (diffColumnsField.length > 0) {
      diffColumnsField.forEach((field) => {
        arrayRemoveByPropertyValue(myColumns, 'field', field)
      })
    }

    tableOptions.columns = mergeColumnsFun(tableParams.columns, myColumns)

    handleRefreshColumn()
  }

  /**
   * 合并列配置
   * @param tableColumns 开发配置的列
   * @param myColumns 自定义的列配置
   */
  function mergeColumnsFun(tableColumns: Array<any>, myColumns: Array<any>) {
    const columns: Array<any> = []
    tableColumns.forEach((col: any) => {
      let foundColumn = null

      if (myColumns && myColumns.length > 0) {
        if (col.field) {
          foundColumn = myColumns.find((c) => c.field === col.field)
        } else if (col.type) {
          foundColumn = myColumns.find((c) => c.type === col.type)
        }
      }

      if (foundColumn) {
        Object.assign(col, foundColumn)
      }

      columns.push(col)
    })

    return columns
  }

  /**
   * 递归初始化开发配置列
   */
  function handleTableOptionsColumnsInit(columns: Array<any>) {
    columns.forEach((item) => {
      if (stringUtils.isNotEmpty(item?.params?.dict)) {
        // item.formatter = 'formatDict'
        if (!item?.slots?.default) {
          assign(item, { slots: { default: 'slotDict' } })
        }
      }

      // 如果是复选框，禁止出现省略号
      if (item.type == 'checkbox') {
        item.showOverflow = false
      }

      if (item.visible === undefined) {
        item.visible = true
      }

      if (item.children && item.children.length > 0) {
        handleTableOptionsColumnsInit(item.children)
      }
    })
  }

  //////////////////////////////////////////////////
  // 导出Excel
  //////////////////////////////////////////////////

  /**
   * 自定义列属性
   */
  const exportExcel = reactive({
    visible: false,
    dataRange: 'currentData' // 导出数据范围
  })

  /**
   * 打开导出Excel
   */
  function handleOpenExportExcel() {
    exportExcel.visible = true
  }

  /**
   * 关闭导出Excel
   */
  function handleCloseExportExcel() {
    exportExcel.visible = false
  }

  /**
   * 导出Excel
   */
  function handleExportExcel() {
    nextTick(() => {
      // 排序参数
      let queryParams: any = getSortParams()

      // 分页参数
      if (tableOptions.pagerConfig?.enabled && exportExcel.dataRange === 'currentData') {
        assign(queryParams, {
          pageNum: tablePage.currentPage,
          pageSize: tablePage.pageSize
        })
      } else {
        assign(queryParams, {
          enable: false
        })
      }

      // 设置导出表头
      const headers: Array<any> = []
      tableOptions.columns?.forEach((item: any) => {
        if (item.field && item.visible) {
          if (!['checkbox'].includes(item.type) && !['操作'].includes(item.title)) {
            const columnWidth = item.width || item.minWidth || 100
            const width = new BigNum(columnWidth).multiply(0.026458).multiply(5).parse()

            const header = {
              title: item.title,
              field: item.field,
              width: width,
              ...(item.params && item.params.dict && { dict: item.params.dict })
            }

            if ('formatAmount' === item.formatter) {
              header.type = 10
              header.isStatistics = true
            }

            headers.push(header)
          }
        }
      })

      if (headers.length > 0) {
        queryParams.headers = headers
      }

      emit('exportExcel', queryParams)

      handleCloseExportExcel()
    })
  }

  //////////////////////////////////////////////////
  // 生命周期
  //////////////////////////////////////////////////
  onMounted(() => {
    merge(tableOptions, props.options)

    if (tableOptions.columns) {
      handleTableOptionsColumnsInit(tableOptions.columns)
      tableParams.columns = cloneDeep(tableOptions.columns) as []
      setTableOptionsColumns()
    }

    if (tableOptions.autoLoad) {
      handleSearch(true)
    }
  })

  //////////////////////////////////////////////////
  // 暴露出去，父组件才可以调用到
  //////////////////////////////////////////////////
  defineExpose({
    tableInstance,
    tableOptions,
    handleQuery,
    handleReload,
    getSelectRows
  })
</script>
